重构中
前言
由于现在大多数公司安全认证基本都使用的是 Token 方式,而以前却没怎么接触过,因此系统性的了解了一下相关历史,并小小的总结了一下。
对互联网目前的安全认证机制,大概主要分为以下三种方式:
- HTTP Basic Auth
- Cookie Based Auth
- Token Based Auth
那么,它们各自的定义是什么,对应的认证流程又是怎样的,各自又有什么样的特点呢?
跟随本文来了解一下吧!
HTTP Basic Auth
简介
HTTP Basic Auth 机制会要求客户端(如浏览器)每次请求服务器时都携带上用户名和密码。
认证流程
对 HTTP Basic Auth 认证机制是怎样的呢?
简单来讲,就是每次请求服务器的时候携带用户名和密码,当然,这个信息会经过简单的加密。
具体而言,该认证机制首先会在 Headers 新增如下格式的字段:
1 | Authorization: Basic <credentials> |
请求服务器的时候将携带该 Headers 进行认证。
详细说明下,credentials是由以下三部分拼接后再经bash 64编码的字符串:
- 用户名
- 冒号
: - 密码
举个例子,浏览器若使用Lovike作为用户名,wozhenbang作为密码,则拼接后为:1
Lovike:wozhenbang
之后,将上述字符串经 bash 64编码变为TG92aWtlOndvemhlbmJhbmc=。
最后,请求服务器的时候将携带下面的 Headers 进行认证:1
Authorization: Basic TG92aWtlOndvemhlbmJhbmc
缺点
由于该方式有把用户名及密码暴露给第三方的危险,因此现在几乎不再使用。
Cookie Based Auth
简介
Cookie Based Auth 机制是很多小型网站常用的认证方式,一般都会结合 Session 来使用。
Cookie 和 Session 的详细介绍请参考此分类下的另一篇文章。
认证流程
Cookie Based Auth 机制的认证流程一般会经过以下步骤:
- ① 用户在登录表单中提供用户名和密码,然后点击登录按钮发起第一次请求
- ② 发出请求后,服务器在后端通过查询数据库来来验证用户
- 若验证成功,将根据从数据库获取的用户信息(Java 会封装为一个类)并将其存储 Session 中
- 对于每个 Session,服务器都会创建一个唯一键值对(如 JSESSIONID=d123asfmore),并自动将其设置到 Cookie 中,在响应时传回浏览器
- ③ 浏览器后续的每个请求都会携带此键值对,服务器会据其去数据库验证相关用户信息(一般会在拦截器统一处理)。换而言之,基于此键值对,将识别请求属于哪个客户端,然后显示相关数据。、
- ④ 最后,用户注销后,在客户端和服务器端的相关信息(Cookie、Session)将在一定时间后被销毁(这取决于被设置的时长)
缺点
对 Cookie Based Auth 而言,若 Session ID 被第三方盗走,对方就可以身份进行恶意操作了。因此必须防止 Session ID 被盗或被猜出。
为了做到这点,Session ID 应使用难以推测的字符串,并且服务器端也需要进行有效期的管理,来保证其安全性。
另外,为减轻跨站脚本攻击(XSS)造成的损失,建议事先在 Cookie 内加上 httponly 属性。
Token Based Auth
简介
与 Cookie Based Auth 相比,Token Based Auth 机制最大的特点是:在服务端不再需要存储用户的登录记录,而是基于一定规则生成一个时效性的令牌(Token),此令牌就代表了相关用户,后续用户就可以使用该令牌来证明身份无需再次登录。
认证流程
该方式的具体认证流程如下:
- ① 用户在登录表单中提供用户名和密码,然后点击登录按钮发起第一次请求
- ② 服务器接收请求后,后端去数据库校验用户名和密码。若验证成功,则服务器会生成一个 Token(其可以存储到 Cookie、HTTP Headers、MySQL、Redis 中),并将其发送给客户端
- ③ 客户端接收到 Token 后会将其存储起来(如放到 Cookie 中,HTTP Headers中),并在后续每次向服务端请求资源时都携带该 Token
- ④ 服务器收到请求,后端去具体存储位置( MySQL、Redis)验证客户端 Token,若验证成功,则返回给客户端请求的数据
说明一下,Token 为什么可以存储在 Cookie、HTTP Headers、MySQL、Redis 中:
- 存储在 Cookie 或 HTTP Headers 中是针对客户端而言,因为其访问服务器时需要携带该 Token 来表明身份
- 存储在 MySQL 或 Redis 中是针对服务器而言,因为其要验证客户端携带的 Token
当然,对服务器而言,使用 Redis 存取 Token 无疑比 MySQL 效率更高。
缺点
由于该方式需要在服务器每次接收到客户端发送的 Token 后,去数据库校验以查询相关数据,因此现在更常用 JWT 来替代该方式
JWT(JSON Web Token) Auth
简介
JSON Web Token(JWT)是一种 Internet 规定的用于创建以 JSON 为基础的访问令牌(一段存取了某些信息的加密字符串)的标准。
与 Token 相比,服务器仍然会生成一个令牌提供给客户端,客户端仍然需要携带该令牌证明身份。
令牌由一方的私钥(通常是服务器的私钥)所签名,因此,双方(另一方已经通过某种适当且可信赖的方式拥有了相应的公钥)能够验证令牌是否合法。
令牌设计的很简洁且是 URL 安全的,并且特别适用于 Web 浏览器单点登录(SSO)的应用。
JWT 经常用于在身份提供商和服务提供商之间传递经过身份认证的用户的信息,或在其他业务流程传递类似的信息。
基本组成
一个 JWT 实际上就是一个字符串,它由三部分组成:
- 头部(Header)
- 载荷(Payload)
- 签名(Signature)
头部(Header)
JWT 的头部用于描述一些最基本的信息,例如其类型及签名所用的算法等。
头部(Header)可以被表示成一个 JSON 对象,如:1
2
3
4{
"typ": "JWT",
"alg": "HS256"
}
在这里,我们说明了这是一个 JWT,并且签名算法为 HS256 。
对它进行 Base64 编码后的字符串就成了 JWT 的 Header(头部)。
1 | eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 |
载荷(Payload)
载荷(Payload)也可被描述成一个 JSON 对象,其基本信息如下:1
2
3
4
5
6
7{
"iss": "Lovike Wong JWT",
"iat": 1441603502,
"exp": 1441604722,
"aud": "www.lovike.cn",
"sub": "lovike@163.com"
}
这里面的前五个字段都是由 JWT 的标准所定义的。
- iss:JWT 的签发者
- iat(issued at):签发时间
- exp(expires):过期时间,为 Unix 时间戳
- sub:所面向的用户
- aud:接收该 JWT 的一方
当然,其中也可以添加一些自定义的信息,如用户的相关信息:1
2
3
4{
"country": "中国",
"age": "18"
}
将上面的 JSON 对象进行 Base64 编码后就得到了 JWT 的Payload(载荷)。1
eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9
签名(Signature)
签名(Signature)是将头部和载荷编码后使用密钥(secret)加密后的数据。
具体流程首先需要将头部和载荷编码后的字符串用句号.连接在一起,形成如下字符串:
1 | eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0 |
之后,我们需要对该字符串用 HS256 算法进行加密,加密时需要提供一个密钥(secret),以保证安全性。
如果我们用lovike作为密钥的话,那么就可以得到我们加密后的内容,这一部分就叫做签名(Signature):
1 | rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM |
完整的 JWT
最后将头部、载荷、签名用句号.拼接在一起就得到了完整的 JWT:
1 | eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM |
认证流程
JWT 的认证流程又是怎样的呢?
对该方式而言,具体的认证流程步骤如下:
- ① 用户在登录表单中提供用户名和密码,然后点击登录按钮发起第一次请求
- ② 服务器接收请求并验证成功后,根据相关信息创建一个加密的 JWT ,其会返回给客户端
- ③ 在后续的交互请求中 JWT 信息将放入 Headers (或
Cookie)中,用户请求服务器时将携带它 - ④ 服务端接收到客户端的请求,将对客户端携带的 JWT 进行解密操作:
- 解密成功则验证通过(允许进行接口调用,返回一些信息给客户端)
- 失败则说明 Token 无效或者已过期(提示用户重新登录)
优点——为什么使用 JWT?
JWT 的优点很多:
- 易解析:JWT 基于 JSON
- 易扩展:在 JWT 中可自定义丰富的内容
- 安全性高:JWT通过非对称加密算法及数字签名技术,防止篡改
- 无状态性:加密后的信息**存储于用户端,服务端无需像 Token 一样查库只需解密即可
- 资源服务使用 JWT 可不依赖认证服务即可完成授权
无状态性详细说明:对常见的 Token 而言,它是存储在数据库中的,需要在服务器去数据库查询并校验 Token 是否合法。
JWT 的意义在于将加密后的信息存储于用户端,服务端只需要解密即可,不需要查库即可获得类似存储在 Session (或 Redis、数据库)中的数据。
换而言之,使用 JWT 后在服务端只需使用密钥校验即可,不用再去查询数据库,这无疑大大地提高了效率。
缺点
JWT 占用的存储空间会比较大。
OAuth
简介
开放授权(OAuth)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
OAuth 允许用户提供一个令牌(而不是用户名和密码)来访问他们存放在特定服务提供者的数据。
每一个令牌都可授权一个特定的网站(如视频编辑网站)在特定的时段(如接下来的 2 小时)内访问特定的资源(例如仅仅是某一相册中的视频)。
这样,OAuth 让用户授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。
运行流程
角色组成
要想理解 OAuth 的运行流程,必须先认识四个重要的角色:
Client:客户端,指需要获取用户资源的第三方应用,如 Gitee 网站Resource Owner:资源所有者,通常指用户,例如微信用户Authorization Server:授权服务器,用于验证资源所有者,验证成功后向客户端颁发访问令牌。例如,微信授权登录页面Resource Server:资源服务器,指存放用户受保护资源的服务器,通常需要通过 Access Token(访问令牌)才能进行访问
资源所有者同意以后,资源服务器可以向客户端颁发令牌,客户端通过令牌,就能去请求资源服务的数据。
令牌的颁发
OAuth 的核心即向第三方应用颁发令牌,只有拿到了令牌才能获取用户的资源信息。
在 OAuth 2.0 标准中,规定了令牌的四种颁发方式:
- 密码式(password)
- 隐藏式(implicit)
- 授权码(authorization-code)
- 客户端凭证(client credentials)
密码式
隐藏式
授权码
客户端凭证
参考
- 维基百科 —— HTTP Basic Auth
- stackoverflow - how-does-cookie-based-authentication-work
- 维基百科 —— JWT
- 维基百科 —— OAuth
- 上野 宣.图解 HTTP [M]. 人民邮电出版社,2014